<template>
  <div>
    <q-field
      :model-value="modelValue"
      :rules="rules"
      :clearable="clearable"
      :readonly="readonly"
      :label="label"
      :bottom-slots="bottomSlots"
      @clear="clearSelect"
    >
      <template #control> {{ displayLabel }} </template>
      <template #append><q-icon :name="icon" /></template>
      <q-menu v-model="menu" fit>
        <q-tree
          ref="treeRef"
          v-model:expanded="expanded"
          :selected="multiple ? undefined : modelValue ?? null"
          :ticked="multiple ? modelValue ?? [] : undefined"
          :nodes="nodes"
          no-selection-unset
          :node-key="nodeKey"
          :label-key="labelKey"
          :children-key="childrenKey"
          :tick-strategy="tickStrategy"
          :default-expand-all="defaultExpandAll"
          :accordion="accordion"
          @update:selected="updateSelected"
          @update:ticked="updateTicked"
        />
      </q-menu>
    </q-field>
  </div>
</template>

<script lang="ts" setup>
  import { computed, ref, watch } from 'vue';
  import { QField, QTree, QTreeNode, ValidationRule } from 'quasar';
  import { mdiMenuDown, mdiMenuUp } from '@quasar/extras/mdi-v6';

  interface Props {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    modelValue: any;
    emitModel?: boolean;
    multiple?: boolean;
    label?: string;
    rules?: ValidationRule[];
    clearable?: boolean;
    readonly?: boolean;
    bottomSlots?: boolean;
    // tree props
    nodes: QTreeNode[];
    nodeKey?: string;
    expandLevel?: number;
    labelKey?: string;
    childrenKey?: string;
    tickStrategy?: 'none' | 'strict' | 'leaf' | 'leaf-filtered' | undefined;
    defaultExpandAll?: boolean;
    accordion?: boolean;
  }

  const props = withDefaults(defineProps<Props>(), {
    display: undefined,
    multiple: false,
    label: undefined,
    rules: undefined,
    clearable: false,
    //tree props
    expandLevel: 1,
    nodeKey: 'value',
    labelKey: 'label',
    childrenKey: 'children',
    tickStrategy: 'strict',
    accordion: true,
  });

  const emit = defineEmits(['update:modelValue']);

  const treeRef = ref<QTree>();
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const expanded = ref<any[]>([]);
  const tickStrategy = computed(() => (props.multiple ? props.tickStrategy : 'none'));

  const menu = ref<boolean | undefined>();
  const icon = computed(() => (menu.value ? mdiMenuUp : mdiMenuDown));

  function updateSelected(v: string | number | null) {
    const model = props.emitModel === true && v !== null ? getNodeByKey(v as string) : undefined;
    emit('update:modelValue', v, model);
    menu.value = false;
  }

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  function updateTicked(ticked: readonly any[]) {
    const models = props.emitModel === true && ticked.length > 0 ? ticked.map((a) => getNodeByKey(a as string)) : undefined;
    emit('update:modelValue', ticked, models);
  }

  function clearSelect() {
    if (props.multiple) {
      updateTicked([]);
    } else {
      updateSelected(null);
    }
  }

  // 获取显示的值
  const displayLabel = computed(() => {
    if (props.modelValue === '' || props.modelValue === undefined || props.modelValue === null || props.nodes.length == 0) {
      return null;
    }

    const keys = props.multiple ? (props.modelValue as []) : [props.modelValue];
    if (keys.length === 0) return null;

    var nodes = keys.map((key) => getNodeByKey(key)).filter((a) => a != null && a != undefined);
    return nodes.map((a) => a[props.labelKey]).join(',');
  });

  // 根据key获取节点
  function getNodeByKey(key: string | number) {
    const reduce = [].reduce;
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const find = (result: any, node: any): any => {
      if (result || !node) {
        return result;
      }
      if (Array.isArray(node) === true) {
        return reduce.call(Object(node), find, result);
      }
      if (node[props.nodeKey] === key) {
        return node;
      }
      if (node[props.childrenKey]) {
        return find(null, node[props.childrenKey]);
      }
    };
    return find(null, props.nodes);
  }

  // 根据需要的展开层级设置默认展开节点
  if (props.expandLevel > 0) {
    watch(
      () => props.nodes.length,
      () => (expanded.value = getExpandKeys()),
      { immediate: true },
    );
  }

  // 根据级别获取要展开的节点key
  function getExpandKeys() {
    if (props.nodes.length == 0) return [];

    const expandedNodes = [];
    let childen = [...props.nodes];
    let i = 0;
    while (i < props.expandLevel && childen.length > 0) {
      expandedNodes.push(...childen);
      childen = childen.flatMap((t) => t.children ?? []);
      i++;
    }

    return expandedNodes.map((a) => a[props.nodeKey]);
  }
</script>
